package org.example.websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.sun.media.jfxmedia.logging.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import org.json.JSONObject;

import java.sql.*;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static com.sun.media.jfxmedia.logging.Logger.*;
import static org.example.websocket.WebSocketServer.*;

/**
 * WebSocket服务器处理器，用于处理WebSocket请求和响应,继承了ChannelInboundHandlerAdapter，覆盖了很多方法。
 * @author Cary
 * @version 1.0
 */
public class WebSocketServerHandler extends ChannelInboundHandlerAdapter {
    //定义饼状图所需的变量，用于保存上一次发送的随机数
    private int laseRandomNumber = -1;
    //定义折线图所需的随机数变量,用于保存上一次发送的随机数
    private int lastRandomNumber = 0;
    //定义枚举类型，用于指定图表类型
    enum ChartType{
        LINE_CHART,
        PIE_CHART,
        MESSAGE
    }
    private Connection conn;
    private ChannelHandlerContext ctx;

    /** 当客户端和服务器建立连接时被调用，用于向客户端定时发送数据。
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        //创建折线图线程，0-5之间的随机数的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //创建一个生成0-5之间随机数的实例
                RandomGenerator generator = new ZeroToFiveRandomGenerator();
                //调用sandRandomNumber方法，传入生成0-5之间随机数的实例和休眠时间
                try {
                    sendRandomNumber(ctx,generator,1000,ChartType.LINE_CHART);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
        //创建饼状图实例，0-100之间随机数的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //创建一个生成0-100之间随机数的实例
                RandomGenerator generator = new ZeroToOneHundredRandomGenerator();
                //调用sendRandomNumber方法，传入生成0-100之间随机数的实例和休眠时间
                try {
                    sendRandomNumber(ctx,generator,5000,ChartType.PIE_CHART);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

    /**
     * 定义一个接口用于生成随机数
     */
    interface RandomGenerator {
        int generateRandomNumber();
    }

    /**
     *  实现一个生成 0-10 之间随机数的类
     */
    class ZeroToFiveRandomGenerator implements RandomGenerator {
        private Random random;

        public ZeroToFiveRandomGenerator() {
            this.random = new Random();
        }

        @Override
        public int generateRandomNumber() {
            return random.nextInt(11);
        }
    }

    /**
     *  实现一个生成 0-100 之间随机数的类
     */
    class ZeroToOneHundredRandomGenerator implements RandomGenerator {
        private Random random;

        public ZeroToOneHundredRandomGenerator() {
            this.random = new Random();
        }

        @Override
        public int generateRandomNumber() {
            return random.nextInt(101);
        }
    }

    /**
     *用于向客户端发送随机数的方法
     * @param ctx
     * @param generator 随机数
     * @param sleepTime 休眠时间
     * @param chartType 不同线程的不同类型，用于区分不同线程的睡眠时间
     */
    public void sendRandomNumber(ChannelHandlerContext ctx,RandomGenerator generator,int sleepTime,ChartType chartType) throws JsonProcessingException {
        //定义一个变量，用于保存上一次发送的随机数
        int lastRandomNumber = 0;
        //进入死循环
        while (true){
            //生成随机数
            int randomNumber = generator.generateRandomNumber();
            //如果随机数为0,就向前端发送上一次发送的随机数
            if (randomNumber == 0){
                System.out.println(randomNumber);
                randomNumber = lastRandomNumber;
            }else {
                lastRandomNumber = randomNumber;
            }
            //打包数据并添加标识
            Map<String, Object> data = new HashMap<>();
            data.put("type", chartType.toString());
            data.put("value",randomNumber);
            System.out.println(data);
            //发送随机数到前端
            Channel channel = ctx.channel(); //获取当前连接的客户端
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(data);
            TextWebSocketFrame frame = new TextWebSocketFrame(String.valueOf(json));
            //将frame写入到和客户端的连接中。
            channel.writeAndFlush(frame);
            //休眠一段时间
            try{
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 根据不同类似，输出在控制台不同的随机数
     * @param chartType 类型
     * @param randomNumber  随机数
     * @return String
     */
    private String getOutputString(ChartType chartType,int randomNumber){
        if (chartType == ChartType.LINE_CHART){
            return "折线图的随机数为：" + randomNumber;
        }else {
            return "饼状图的随机数为：" + randomNumber;
        }
    }
    /**
     * 在客户端与服务器断开连接时被调用，用于关闭MySql数据库连接。
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception{
        //关闭MySql连接
        if (conn != null){
            conn.close();
        }
        super.channelInactive(ctx);
    }
    //定义了一个AttributeKey类型的常量，用于存储WebSocket握手器对象，用于完成握手后的数据交换。
    private static final AttributeKey<WebSocketServerHandshaker> ATTR_HANDSHAKER = AttributeKey.valueOf("handshaker");

    /**
     * 处理客户端发送的请求，并根据请求类型进行相应的处理，适用于同时处理HTTP请求和WebSocket消息。
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) { //如果是http请求
            // 处理 HTTP 请求
            FullHttpRequest request = (FullHttpRequest) msg;
            handleHttpRequest(ctx, request);
        } else if (msg instanceof WebSocketFrame) { //如果是WebSocketFreame对象，
            // 处理 WebSocket 消息
            WebSocketFrame frame = (WebSocketFrame) msg;
            handleWebSocketFrame(ctx, frame);
        }
        if (msg instanceof TextWebSocketFrame){ //如果是一个TextWebSocketFrame对象，说明客户端发送了一个文本消息
            //处理文本消息
            String message = ((TextWebSocketFrame) msg).text();
            System.out.println("Received message:" + message);
            //创建一个map，用于存储数据
            Map<String, Object> data = new HashMap<>();
            //设置标识符为 MESSAGE
            data.put("type",ChartType.MESSAGE);
            //设置数据为hello
            data.put("value","hello, client");
            //将Map转换为JSON字符串并发送给前端
            sendMessage(ctx,new JSONObject(data).toString());
        }
    }

    /**
     * 处理客户端发送的HTTP请求，并完成WebSocket握手过程。
     * @param ctx
     * @param request
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        // 处理 HTTP 请求，完成 WebSocket 握手
        if (!request.decoderResult().isSuccess()) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        //检查HTTP请求的头部是否包含Upgrade字段，并且值是否为WebSocket。
        HttpHeaders headers = request.headers();
        String upgrade = headers.get(HttpHeaderNames.UPGRADE);
        if (!"websocket".equalsIgnoreCase(upgrade)) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        //检查HTTP请求头部是否包含Origin字段
        String origin = headers.get(HttpHeaderNames.ORIGIN);
        if (origin == null) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        //创建一个 WebSocketServerHandshakerFactory 对象，并调用它的 newHandshaker 方法来创建一个 WebSocketServerHandshaker 对象，用于完成 WebSocket 握手过程。
        WebSocketServerHandshakerFactory handshakerFactory = new WebSocketServerHandshakerFactory(getWebSocketURL(request), null, true);
        WebSocketServerHandshaker handshaker = handshakerFactory.newHandshaker(request);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), request);
        }
    }

    /**
     * 处理客户端发送的WebSocket消息，检查frame对象的类型并做不同的处理，适用于只处理WebSocket消息。
     * @param ctx
     * @param frame
     */
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        // 处理 WebSocket 消息
        if (frame instanceof TextWebSocketFrame) {
            String message = ((TextWebSocketFrame) frame).text();
            System.out.println("Received message: " + message);
            ctx.write(new TextWebSocketFrame("Hello, " + message + "!"));
        } else if (frame instanceof BinaryWebSocketFrame) {
            // 处理二进制消息
        } else if (frame instanceof PingWebSocketFrame) {
            // 处理 Ping 消息
            ctx.write(new PongWebSocketFrame(frame.content().retain()));
        } else if (frame instanceof PongWebSocketFrame) {
            // 处理 Pong 消息
        } else if (frame instanceof CloseWebSocketFrame) {
            // 处理关闭消息
            WebSocketServerHandshaker handshaker = ctx.channel().attr(ATTR_HANDSHAKER).get();
            if (handshaker != null) {
                handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            }
        }
    }

    /**
     * 用于向客户端发送HTTP响应
     * @param ctx
     * @param request
     * @param response
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
        // 发送 HTTP 响应
        if (response.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            HttpUtil.setContentLength(response, response.content().readableBytes());
        }

        ChannelFuture future = ctx.channel().writeAndFlush(response);
        if (!HttpUtil.isKeepAlive(request) || response.status().code() != 200) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }
    private String getWebSocketURL(FullHttpRequest request) {
        String protocol = "ws";
        if (request.headers().get(HttpHeaderNames.HOST).contains("https")) {
            protocol = "wss";
        }
        return protocol + "://" + request.headers().get(HttpHeaderNames.HOST) + request.uri();
    }
    //向客户端发送消息，ctx参数代表客户端的对象，nessage参数代表要发送的消息内容
    private void sendMessage(ChannelHandlerContext ctx,Object message){
        TextWebSocketFrame frame = new TextWebSocketFrame((String) message);
        ctx.writeAndFlush(frame);
        System.out.println("发送消息到客户端："+message);
    }

}
